// 《围棋》作者版权所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 该文件实现了语法树结构的打印。

package syntax

import (
	"fmt"
	"io"
	"reflect"
	"unicode"
	"unicode/utf8"
)

// Fdump转储以n到w为根的语法树的结构。
// 用于调试目的；不保证使用特定的输出格式
// 。
func Fdump(w io.Writer, n Node) (err error) {
	p := dumper{
		output: w,
		ptrmap: make(map[Node]int),
		last:   '\n', // 强制打印第一行的行号
	}

	defer func() {
		if e := recover(); e != nil {
			err = e.(writeError).err // 如果不是写入者，则重新恐慌
		}
	}()

	if n == nil {
		p.printf("nil\n")
		return
	}
	p.dump(reflect.ValueOf(n), n)
	p.printf("\n")

	return
}

type dumper struct {
	output io.Writer
	ptrmap map[Node]int // 节点->转储行号
	indent int          // 当前缩进级别
	last   byte         // 写入的最后一个字节
	line   int          // 当前行号
}

var indentBytes = []byte(".  ")

func (p *dumper) Write(data []byte) (n int, err error) {
	var m int
	for i, b := range data {
		// 不变量：数据[0:n]已写入
		if b == '\n' {
			m, err = p.output.Write(data[n : i+1])
			n += m
			if err != nil {
				return
			}
		} else if p.last == '\n' {
			p.line++
			_, err = fmt.Fprintf(p.output, "%6d  ", p.line)
			if err != nil {
				return
			}
			for j := p.indent; j > 0; j-- {
				_, err = p.output.Write(indentBytes)
				if err != nil {
					return
				}
			}
		}
		p.last = b
	}
	if len(data) > n {
		m, err = p.output.Write(data[n:])
		n += m
	}
	return
}

// 写入者错误本地捕获的写入错误，因此我们可以区分
// 它们与我们不希望返回为错误的真正恐慌。
type writeError struct {
	err error
}

// printf是一种方便的包装器，可以处理打印错误。
func (p *dumper) printf(format string, args ...interface{}) {
	if _, err := fmt.Fprintf(p, format, args...); err != nil {
		panic(writeError{err})
	}
}

// 转储打印x的内容。
// 如果x是反射。结构s的值，其中&s 
// 实现节点，则应将&s传递给n-
// 这允许通过
// 注释字段。
// 调用span（）和Comment（），而不是使用
// 反射来打印嵌入isNode字段的未报告的span和
func (p *dumper) dump(x reflect.Value, n Node) {
	switch x.Kind() {
	case reflect.Interface:
		if x.IsNil() {
			p.printf("nil")
			return
		}
		p.dump(x.Elem(), nil)

	case reflect.Ptr:
		if x.IsNil() {
			p.printf("nil")
			return
		}

		// 没有附加注释的标识符的特殊情况（常见情况）
		if x, ok := x.Interface().(*Name); ok {
			p.printf("%s @ %v", x.Value, x.Pos())
			return
		}

		p.printf("*")
		// 字段可能共享类型表达式，声明
		// 可能共享同一组-使用ptrmap跟踪已经打印的节点的
		// 情况。
		if ptr, ok := x.Interface().(Node); ok {
			if line, exists := p.ptrmap[ptr]; exists {
				p.printf("(Node @ %d)", line)
				return
			}
			p.ptrmap[ptr] = p.line
			n = ptr
		}
		p.dump(x.Elem(), n)

	case reflect.Slice:
		if x.IsNil() {
			p.printf("nil")
			return
		}
		p.printf("%s (%d entries) {", x.Type(), x.Len())
		if x.Len() > 0 {
			p.indent++
			p.printf("\n")
			for i, n := 0, x.Len(); i < n; i++ {
				p.printf("%d: ", i)
				p.dump(x.Index(i), nil)
				p.printf("\n")
			}
			p.indent--
		}
		p.printf("}")

	case reflect.Struct:
		typ := x.Type()

		// 如果是span，确定：=x.Interface（）。（词汇跨度）；好的{
		// p.printf（“%s”，和span）
		// /返回
		// }

		p.printf("%s {", typ)
		p.indent++

		first := true
		if n != nil {
			p.printf("\n")
			first = false
			// /如果c:=*n.Comments（）；c！=nil{
			// p.printf（“注释：”）
			// p.dump（reflect.ValueOf（c），nil）
			// p.printf（“\n”）
			// }
		}

		for i, n := 0, typ.NumField(); i < n; i++ {
			// 排除非导出字段，因为它们的
			// 值无法通过反射访问。
			if name := typ.Field(i).Name; isExported(name) {
				if first {
					p.printf("\n")
					first = false
				}
				p.printf("%s: ", name)
				p.dump(x.Field(i), nil)
				p.printf("\n")
			}
		}

		p.indent--
		p.printf("}")

	default:
		switch x := x.Interface().(type) {
		case string:
			// 用引号打印字符串
			p.printf("%q", x)
		default:
			p.printf("%v", x)
		}
	}
}

func isExported(name string) bool {
	ch, _ := utf8.DecodeRuneInString(name)
	return unicode.IsUpper(ch)
}
